package cn.template.serve_project.service.impl;

import cn.template.serve_project.constant.JwtConstant;
import cn.template.serve_project.dto.excel.ExcelUserDTO;
import cn.template.serve_project.dto.login.LoginWXDTO;
import cn.template.serve_project.dto.user.UserAddDTO;
import cn.template.serve_project.dto.user.UserPageDTO;
import cn.template.serve_project.dto.user.UserUpdateDTO;
import cn.template.serve_project.entity.Role;
import cn.template.serve_project.entity.RoleUser;
import cn.template.serve_project.entity.User;
import cn.template.serve_project.enums.CommonStateEnum;
import cn.template.serve_project.enums.R;
import cn.template.serve_project.enums.RoleEnum;
import cn.template.serve_project.excelObj.ExcelUser;
import cn.template.serve_project.exception.BusinessException;
import cn.template.serve_project.mapper.AdminMapper;
import cn.template.serve_project.mapper.RoleMapper;
import cn.template.serve_project.mapper.RoleUserMapper;
import cn.template.serve_project.mapper.UserMapper;
import cn.template.serve_project.service.ExcelService;
import cn.template.serve_project.service.LoginService;
import cn.template.serve_project.service.UserService;
import cn.template.serve_project.util.ExcelUtil;
import cn.template.serve_project.util.JwtUtil;
import cn.template.serve_project.util.QueryUtil;
import cn.template.serve_project.vo.login.LoginUserVO;
import cn.template.serve_project.vo.login.WxChat;
import cn.template.serve_project.vo.user.UserPageVO;
import cn.template.serve_project.vo.user.UserVO;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

/**
 * 用户表(User)表服务实现类
 */
@Service("userService")
@Transactional
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService, LoginService<LoginWXDTO, LoginUserVO>, ExcelService<ExcelUserDTO> {

    @Value("${rxsq.wechat.appid}")
    private String APP_ID; //appid

    @Value("${rxsq.wechat.secret}")
    private String SECRET; //密钥

    //请求openid路径
    private static final String WX_LOGIN = "https://api.weixin.qq.com/sns/jscode2session";

    private final UserMapper userMapper;
    private final RestTemplate restTemplate;
    private final AdminMapper adminMapper;

    private final RoleMapper roleMapper;

    private final RoleUserMapper roleUserMapper;

    public UserServiceImpl(UserMapper userMapper, RoleMapper roleMapper, AdminMapper adminMapper, RoleUserMapper roleUserMapper, RestTemplate restTemplate) {
        this.userMapper = userMapper;
        this.restTemplate = restTemplate;
        this.adminMapper = adminMapper;
        this.roleUserMapper = roleUserMapper;
        this.roleMapper = roleMapper;
    }

    /**
     * 微信登录接口
     *
     * @param dto 参数对象
     * @return 登录信息
     */
    @Override
    public LoginUserVO login(LoginWXDTO dto) {
        //获取openid
        String openid = getOpenid(dto.getCode());
        if (openid == null || "".equals(openid)) {
            throw new BusinessException(R.ERROR_AUTHORIZED);
        }

        //返回登录信息的VO
        LoginUserVO loginUserVO = new LoginUserVO();

        //查询用户信息
        UserVO userInfo = userMapper.selectByOpenid(openid);

        //查询的用户信息不为null,则进行开始封装登录信息
        if (userInfo != null) {
            BeanUtils.copyProperties(userInfo, loginUserVO.getUser());
        } else {
            //注册，封装登录信息
            BeanUtils.copyProperties(this.enroll(openid), loginUserVO.getUser());
        }


        //生成token,并存入
        Map<String, Object> info = new HashMap<>();
        info.put(JwtConstant.USER, loginUserVO.getUser().getUserId());
        long duration = 1000 * 60 * 60 * 24 * 7L;
        String token = JwtUtil.createToken(String.valueOf(loginUserVO.getUser().getUserId()), duration, info);
        loginUserVO.setToken(token);

        //返回登录信息
        return loginUserVO;
    }


    //远程请求微信，获取openid 用户唯一标识
    public String getOpenid(String code) {
        //请求路径
        String url = WX_LOGIN + "?appid=" + APP_ID + "&secret=" + SECRET + "&js_code=" + code + "&grant_type=authorization_code";
        //远程请求微信 ，请求微信授权
        String forObject = restTemplate.getForObject(url, String.class);
        WxChat wxChat = JSON.parseObject(forObject, WxChat.class);
        return Objects.isNull(wxChat) ? null : wxChat.getOpenid();
    }

    /**
     * 注册
     *
     * @param openid 微信唯一标识
     * @return 用户信息
     */
    public UserVO enroll(String openid) {
        User user = new User();
        UserVO userInfo = new UserVO();
        //默认用户名
        user.setNickname("软星" + System.currentTimeMillis());
        user.setAvatar("https://www.lyzhnb.top/waxl/QQ%E5%9B%BE%E7%89%8720240314093823.jpg");
        user.setOpenid(openid);
        user.setState(CommonStateEnum.OK.code);
        userMapper.insert(user);
        if (user.getUserId() == null) {
            throw new BusinessException(R.ERROR_ENROLL);
        }
        BeanUtils.copyProperties(user, userInfo);
        return userInfo;
    }

    /**
     * 新增用户
     *
     * @param dto 参数对象
     */
    @Override
    public void addUser(UserAddDTO dto) {

        //检查传递的角色id合法性
        LambdaQueryWrapper<Role> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Role::getRoleId, dto.getRole());
        Role role = roleMapper.selectOne(queryWrapper);
        Optional.ofNullable(role).orElseThrow(() -> new BusinessException(R.ERROR_PERMISSION));
        if (role.getRoleValue() >= RoleEnum.ADMIN.value) {
            throw new BusinessException(R.ERROR_PERMISSION);
        }

        QueryUtil.validateName(dto.getNickname());
        QueryUtil.validatePhone(dto.getPhone());

        //检查手机号是否已被使用
        LambdaQueryWrapper<User> userWrapper = new LambdaQueryWrapper<>();
        userWrapper.eq(User::getPhone, dto.getPhone());
        if (userMapper.selectList(userWrapper).size() > 0) {
            throw new BusinessException(R.ERROR_PHONE_USED);
        }


        User user = new User();
        BeanUtils.copyProperties(dto, user);
        user.setState(CommonStateEnum.OK.code);

        if (userMapper.insert(user) < 1) {
            throw new BusinessException(R.ERROR_ENROLL);
        }

        RoleUser roleUser = new RoleUser();
        roleUser.setUserId(user.getUserId());
        roleUser.setRoleId(dto.getRole());
        if (roleUserMapper.insert(roleUser) < 1) {
            throw new BusinessException(R.ERROR_ENROLL);
        }
    }

    /**
     * 分页查询用户信息
     *
     * @param dto 参数对象
     * @return 数据列表
     */
    @Override
    public UserPageVO selectPage(UserPageDTO dto) {
        User user = new User();
        BeanUtils.copyProperties(dto, user);
        dto.setPage((dto.getPage() - 1) * dto.getSize());
        List<UserVO> dataList = userMapper.selectPageWithRoles(user, dto.getPage(), dto.getSize(), dto.getRole());
        Long total = userMapper.countPageWithRoles(user, dto.getRole());
        UserPageVO result = new UserPageVO();
        result.setDataList(dataList);
        result.setTotal(total);
        return result;
    }

    /**
     * 根据id列表删除
     *
     * @param idList id列表
     * @return 成功数
     */
    @Override
    public Integer deleteInIdList(List<Long> idList) {
        if (idList.size() < 1) {
            return 0;
        }

        LambdaQueryWrapper<RoleUser> roleUserWrapper = new LambdaQueryWrapper<>();
        roleUserWrapper.in(RoleUser::getUserId, idList);
        if (roleUserMapper.delete(roleUserWrapper) < 1) {
            throw new BusinessException(R.ERROR_DELETE);
        }

        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(User::getUserId, idList);
        int delete = userMapper.delete(wrapper);
        if (delete < 1) {
            throw new BusinessException(R.ERROR_DELETE);
        }
        return delete;
    }

    /**
     * 变更用户信息
     *
     * @param dto 参数对象
     */
    @Override
    public void updateUser(UserUpdateDTO dto) {
        Optional.ofNullable(dto.getPhone()).ifPresent(s -> {
            QueryUtil.validatePhone(dto.getPhone());
        });
        Optional.ofNullable(dto.getNickname()).ifPresent(s -> {
            QueryUtil.validateName(dto.getNickname());
        });

        Optional.ofNullable(dto.getRole()).ifPresent(s -> {
            //检查角色id合法性
            LambdaQueryWrapper<Role> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Role::getRoleId, dto.getRole()).select(Role::getRoleValue);
            Role role = roleMapper.selectOne(queryWrapper);
            if (role.getRoleValue() >= RoleEnum.ADMIN.value) {
                throw new BusinessException(R.ERROR_PERMISSION);
            }

            LambdaUpdateWrapper<RoleUser> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(RoleUser::getUserId, dto.getUserId()).set(RoleUser::getRoleId, dto.getRole());
            if (roleUserMapper.update(null, updateWrapper) < 1) {
                throw new BusinessException(R.ERROR_UPDATE);
            }
        });

        LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(User::getUserId, dto.getUserId());
        User user = new User();
        BeanUtils.copyProperties(dto, user);
        if (userMapper.update(user, wrapper) < 1) {
            throw new BusinessException(R.ERROR_UPDATE);
        }
    }


    /**
     * 导出报表
     *
     * @param excelObj 导出对象
     * @param response 网络请求对象
     */
    @Override
    public void export(ExcelUserDTO excelObj, HttpServletResponse response) throws IOException {
        //需要导出数据列表
        List<List<ExcelUser>> dataList = new ArrayList<>();
        //根据id查询
        if (Objects.nonNull(excelObj.getIdList()) && excelObj.getIdList().size() > 0) {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.in(User::getUserId, excelObj.getIdList());
            List<UserVO> users = userMapper.selectInIds(excelObj.getIdList());
            List<ExcelUser> list = new ArrayList<>();
            users.forEach(s -> {
                ExcelUser user = new ExcelUser();
                BeanUtils.copyProperties(s, user);
                user.setRole(s.getRole().getRoleName());
                list.add(user);
            });

            dataList.add(list);
        } else {
            User user = new User();
            BeanUtils.copyProperties(excelObj.getQuery(), user);
            for (int i = 0; i < excelObj.getPageQuery().getPage(); i++) {
                int page = (((i + 1) - 1) * excelObj.getPageQuery().getSize());
                List<UserVO> list = userMapper.selectPageWithRoles(user, page, excelObj.getPageQuery().getSize(), excelObj.getQuery().getRole());
                List<ExcelUser> lists = new ArrayList<>();
                list.forEach(s -> {
                    ExcelUser data = new ExcelUser();
                    BeanUtils.copyProperties(s, data);
                    data.setRole(s.getRole().getRoleName());
                    lists.add(data);
                });
                dataList.add(lists);
            }

        }

        ExcelUtil.export(response,excelObj.getFieldList(),dataList, ExcelUser.class);
    }
}

